﻿namespace Interpreter
{
	internal class OperatorTableBuilder
	{
		public const int OP_ADD = 0;
		public const int OP_SUBTRACT = 1;
		public const int OP_MULTIPLY = 2;
		public const int OP_DIVIDE = 3;
		public const int OP_MOD = 10;
		public const int OP_LESS_THAN = 4;
		public const int OP_LESS_THAN_OR_EQUAL = 5;
		public const int OP_GREATER_THAN = 6;
		public const int OP_GREATER_THAN_OR_EQUAL = 7;
		public const int OP_EQUAL = 8;
		public const int OP_NOT_EQUAL = 9;
		public const int OP_BITWISE_AND = 11;
		public const int OP_BITWISE_OR = 12;
		public const int OP_BITWISE_XOR = 13;
		public const int OP_BITSHIFT_LEFT = 14;
		public const int OP_BITSHIFT_RIGHT = 15;

		public const int OP_MAX = 16;

		public static string GetOpNameFromId(int id)
		{
			if (id == OP_ADD) return "+";
			if (id == OP_SUBTRACT) return "-";
			if (id == OP_MULTIPLY) return "*";
			if (id == OP_DIVIDE) return "/";
			if (id == OP_MOD) return "%";
			if (id == OP_LESS_THAN) return "<";
			if (id == OP_LESS_THAN_OR_EQUAL) return "<=";
			if (id == OP_GREATER_THAN) return ">";
			if (id == OP_GREATER_THAN_OR_EQUAL) return ">=";
			if (id == OP_EQUAL) return "==";
			if (id == OP_NOT_EQUAL) return "!=";
			if (id == OP_BITWISE_AND) return "&";
			if (id == OP_BITWISE_OR) return "|";
			if (id == OP_BITWISE_XOR) return "^";
			if (id == OP_BITSHIFT_LEFT) return "<<";
			if (id == OP_BITSHIFT_RIGHT) return ">>";
			throw new System.NotImplementedException("You added an op to the table, but didn't add it here. ID#: " + id);
		}

		public static int g(int typeLeft, int typeRight, int op)
		{
			return ((typeLeft * Value.TYPE_MAX) + typeRight) * OperatorTableBuilder.OP_MAX + op;
		}

		public int[] ByteCodeToOp { get; private set; }
		public int[] OpMap { get; private set; }

		public OperatorTableBuilder()
		{
			int[] byteCodeToOp = new int[Interpreter.BC_MAX];
			byteCodeToOp[Interpreter.BC_ADD] = OP_ADD;
			byteCodeToOp[Interpreter.BC_SUBTRACT] = OP_SUBTRACT;
			byteCodeToOp[Interpreter.BC_MULTIPLY] = OP_MULTIPLY;
			byteCodeToOp[Interpreter.BC_DIVIDE] = OP_DIVIDE;
			byteCodeToOp[Interpreter.BC_MOD] = OP_MOD;
			byteCodeToOp[Interpreter.BC_BITWISE_AND] = OP_BITWISE_AND;
			byteCodeToOp[Interpreter.BC_BITWISE_OR] = OP_BITWISE_OR;
			byteCodeToOp[Interpreter.BC_BITWISE_XOR] = OP_BITWISE_XOR;
			byteCodeToOp[Interpreter.BC_EQUALS] = OP_EQUAL;
			byteCodeToOp[Interpreter.BC_NOT_EQUAL] = OP_NOT_EQUAL;
			byteCodeToOp[Interpreter.BC_LESS_THAN] = OP_LESS_THAN;
			byteCodeToOp[Interpreter.BC_LESS_THAN_OR_EQUAL] = OP_LESS_THAN_OR_EQUAL;
			byteCodeToOp[Interpreter.BC_GREATER_THAN] = OP_GREATER_THAN;
			byteCodeToOp[Interpreter.BC_GREATER_THAN_OR_EQUAL] = OP_GREATER_THAN_OR_EQUAL;

			this.ByteCodeToOp = byteCodeToOp;

			int max = Value.TYPE_MAX * Value.TYPE_MAX * OperatorTableBuilder.OP_MAX;
			int[] ops = new int[max]; // all zero
			this.OpMap = ops;

			int NULL = Value.TYPE_NULL;
			int INT = Value.TYPE_INTEGER;
			int FLOAT = Value.TYPE_FLOAT;
			int BOOL = Value.TYPE_BOOLEAN;
			int STRING = Value.TYPE_STRING;
			int LIST = Value.TYPE_LIST;
			int INSTANCE = Value.TYPE_INSTANCE;
			int CLASS = Value.TYPE_CLASS;

			int QUICK_FALSE = 1000000;
			int QUICK_TRUE = 1000001;
			int VALUE0_COMPARE = 1000002;
			int VALUE0_COMPARE_INVERSE = 1000003;

			ops[g(INT, INT, OP_ADD)] = 1;
			ops[g(INT, FLOAT, OP_ADD)] = 2;
			ops[g(FLOAT, INT, OP_ADD)] = 3;
			ops[g(FLOAT, FLOAT, OP_ADD)] = 4;
			ops[g(STRING, STRING, OP_ADD)] = 5;
			ops[g(STRING, INT, OP_ADD)] = 6;
			ops[g(STRING, FLOAT, OP_ADD)] = 7;
			ops[g(STRING, BOOL, OP_ADD)] = 8;
			ops[g(INT, STRING, OP_ADD)] = 9;
			ops[g(FLOAT, STRING, OP_ADD)] = 10;
			ops[g(BOOL, STRING, OP_ADD)] = 11;
			ops[g(LIST, LIST, OP_ADD)] = 57;
			ops[g(INT, INT, OP_SUBTRACT)] = 12;
			ops[g(INT, FLOAT, OP_SUBTRACT)] = 13;
			ops[g(FLOAT, INT, OP_SUBTRACT)] = 14;
			ops[g(FLOAT, FLOAT, OP_SUBTRACT)] = 15;
			ops[g(INT, INT, OP_MULTIPLY)] = 16;
			ops[g(INT, FLOAT, OP_MULTIPLY)] = 17;
			ops[g(INT, STRING, OP_MULTIPLY)] = 68;
			ops[g(INT, LIST, OP_MULTIPLY)] = 70;
			ops[g(FLOAT, INT, OP_MULTIPLY)] = 18;
			ops[g(FLOAT, FLOAT, OP_MULTIPLY)] = 19;
			ops[g(STRING, INT, OP_MULTIPLY)] = 67;
			ops[g(LIST, INT, OP_MULTIPLY)] = 69;
			ops[g(INT, INT, OP_DIVIDE)] = 20;
			ops[g(INT, FLOAT, OP_DIVIDE)] = 21;
			ops[g(FLOAT, INT, OP_DIVIDE)] = 22;
			ops[g(FLOAT, FLOAT, OP_DIVIDE)] = 23;
			ops[g(INT, INT, OP_MOD)] = 60;
			ops[g(INT, FLOAT, OP_MOD)] = 61;
			ops[g(FLOAT, INT, OP_MOD)] = 62;
			ops[g(FLOAT, FLOAT, OP_MOD)] = 63;
			ops[g(LIST, STRING, OP_ADD)] = 64;
			ops[g(STRING, LIST, OP_ADD)] = 65;

			ops[g(NULL, NULL, OP_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, BOOL, OP_EQUAL)] = 24;
			ops[g(BOOL, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(BOOL, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, INT, OP_EQUAL)] = 25;
			ops[g(INT, FLOAT, OP_EQUAL)] = 26;
			ops[g(INT, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INT, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, INT, OP_EQUAL)] = 27;
			ops[g(FLOAT, FLOAT, OP_EQUAL)] = 28;
			ops[g(FLOAT, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(FLOAT, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, STRING, OP_EQUAL)] = 29; // TODO: will VALUE0_COMPARE SUFFICE?
			ops[g(STRING, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(STRING, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(LIST, LIST, OP_EQUAL)] = 59;
			ops[g(LIST, INSTANCE, OP_EQUAL)] = VALUE0_COMPARE;
			ops[g(LIST, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(INSTANCE, INSTANCE, OP_EQUAL)] = VALUE0_COMPARE;
			ops[g(INSTANCE, CLASS, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, NULL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, BOOL, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, INT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, FLOAT, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, STRING, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, LIST, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, INSTANCE, OP_EQUAL)] = QUICK_FALSE;
			ops[g(CLASS, CLASS, OP_EQUAL)] = VALUE0_COMPARE;

			ops[g(NULL, NULL, OP_NOT_EQUAL)] = QUICK_FALSE;
			ops[g(NULL, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(NULL, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, BOOL, OP_NOT_EQUAL)] = 30;
			ops[g(BOOL, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(BOOL, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, INT, OP_NOT_EQUAL)] = 31;
			ops[g(INT, FLOAT, OP_NOT_EQUAL)] = 32;
			ops[g(INT, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INT, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, INT, OP_NOT_EQUAL)] = 33;
			ops[g(FLOAT, FLOAT, OP_NOT_EQUAL)] = 34;
			ops[g(FLOAT, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(FLOAT, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, STRING, OP_NOT_EQUAL)] = 35;
			ops[g(STRING, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(STRING, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, LIST, OP_NOT_EQUAL)] = 58;
			ops[g(LIST, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(LIST, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(INSTANCE, INSTANCE, OP_NOT_EQUAL)] = VALUE0_COMPARE_INVERSE;
			ops[g(INSTANCE, CLASS, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, NULL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, BOOL, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, INT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, FLOAT, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, STRING, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, LIST, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, INSTANCE, OP_NOT_EQUAL)] = QUICK_TRUE;
			ops[g(CLASS, CLASS, OP_NOT_EQUAL)] = VALUE0_COMPARE_INVERSE;

			ops[g(INT, INT, OP_LESS_THAN)] = 36;
			ops[g(INT, FLOAT, OP_LESS_THAN)] = 37;
			ops[g(FLOAT, INT, OP_LESS_THAN)] = 38;
			ops[g(FLOAT, FLOAT, OP_LESS_THAN)] = 39;
			ops[g(INT, INT, OP_LESS_THAN_OR_EQUAL)] = 40;
			ops[g(INT, FLOAT, OP_LESS_THAN_OR_EQUAL)] = 41;
			ops[g(FLOAT, INT, OP_LESS_THAN_OR_EQUAL)] = 42;
			ops[g(FLOAT, FLOAT, OP_LESS_THAN_OR_EQUAL)] = 43;
			ops[g(INT, INT, OP_GREATER_THAN)] = 44;
			ops[g(INT, FLOAT, OP_GREATER_THAN)] = 45;
			ops[g(FLOAT, INT, OP_GREATER_THAN)] = 46;
			ops[g(FLOAT, FLOAT, OP_GREATER_THAN)] = 47;
			ops[g(INT, INT, OP_GREATER_THAN_OR_EQUAL)] = 48;
			ops[g(INT, FLOAT, OP_GREATER_THAN_OR_EQUAL)] = 49;
			ops[g(FLOAT, INT, OP_GREATER_THAN_OR_EQUAL)] = 50;
			ops[g(FLOAT, FLOAT, OP_GREATER_THAN_OR_EQUAL)] = 51;

			ops[g(INT, INT, OP_BITSHIFT_LEFT)] = 52;
			ops[g(INT, INT, OP_BITSHIFT_RIGHT)] = 53;

			ops[g(INT, INT, OP_BITWISE_AND)] = 54;
			ops[g(INT, INT, OP_BITWISE_OR)] = 55;
			ops[g(INT, INT, OP_BITWISE_XOR)] = 56;
		}
	}
}
